
package jp.ize.ux;

import flash.system.Capabilities;
import flash.net.URLRequest;

/**
 * SWFAddress 2.3: Deep linking for Flash and Ajax <http://www.asual.com/swfaddress/>
 *
 * SWFAddress is (c) 2006-2009 Rostislav Hristov and contributors
 * This software is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
 *
 */

/**
 * @author Rostislav Hristov <http://www.asual.com>
 * @author Ma Bingyao <andot@ujn.edu.cn>
 */
class SWFAddress
{
	// {{{ properties

	public static var _init         : Bool = false;
	public static var _initChange   : Bool = false;
	public static var _initChanged  : Bool = false;
	public static var _strict       : Dynamic = true;
	public static var _value        : String = '';
	public static var _availability : Bool = flash.external.ExternalInterface.available;
	//public static var _initialized  : Bool = _initializer();
	public static var _initialized  : Bool = false;
	public static var _interval     : haxe.Timer;

	public static var onInit   : Void -> Void;
	public static var onChange : Void -> Void;

	// }}}
	// {{{ public static function _check() : Void

    public static function _check() : Void
	{
        if (SWFAddress.onInit!=null && !_init) {
            SWFAddress._setValueInit(_getValue());
            SWFAddress._init = true;
        }
        if (SWFAddress.onChange!=null) {
            _interval.stop();
            SWFAddress._init = true;
            SWFAddress._setValueInit(_getValue());
        }
    }
   
   	// }}}
	// {{{ public static function _strictCheck(value:String, force:Bool):String

    public static function _strictCheck(value:String, force:Bool):String
	{
        if (SWFAddress.getStrict()) {
            if (force) {
                if (value.substr(0, 1) != '/') value = '/' + value;
            } else {
                if (value == '') value = '/';
            }
        }
        return value;
    }

	// }}}
	// {{{ public static function _getValue()

    public static function _getValue()
	{
        var value:String = null, ids:Array<String> = null;
        if (_availability) {
            value = Std.string(flash.external.ExternalInterface.call('SWFAddress.getValue'));
            //ids = cast(flash.external.ExternalInterface.call('SWFAddress.getIds'));
        }
        if ( ids == null || !_availability || _initChange) {
            value = _value;
        } else if (value == null) {
            value = '';
        }
        return SWFAddress._strictCheck(value , false);
    }
   
   	// }}}
	// {{{ public static function _setValueInit(value)

    public static function _setValueInit(value)
	{
        SWFAddress._value = value;
        if (!_init) {
            SWFAddress._dispatchEvent('init');
        } else {
            SWFAddress._dispatchEvent('change');
        }
        SWFAddress._initChange = true;
    }
   
   	// }}}
	// {{{ public static function _setValue(value)

    public static function _setValue(value)
	{
        if (value == 'undefined' || value == 'null') value = '';
        if (SWFAddress._value == value && SWFAddress._init) return;
        if (!SWFAddress._initChange) return;
        SWFAddress._value = value;
        if (!_init) {
            SWFAddress._init = true;            
            if (SWFAddress.onInit!=null) SWFAddress.onInit();
        }
        SWFAddress._dispatchEvent('change');
    }

	// }}}
	// {{{ public static function _dispatchEvent(type:String)

    public static function _dispatchEvent(type:String)
	{
        type = type.substr(0, 1).toUpperCase() + type.substr(1);
        if (Reflect.field(SWFAddress,'on' + type) != null) {
			var func = Reflect.field(SWFAddress,'on'+type);
			func();
        }
    }

	// }}}
	// {{{ public static function _call(fn, ?param)

    public static function _call(fn, ?param)
	{
        if (param == null) param = '';
        if (_availability) {
            if (Capabilities.os.indexOf('Mac') != -1) {
                //if (param!=null) param = '"' + SWFAddress.encodeURI(SWFAddress.decodeURI(cast(param))) + '"';
                if (param!=null) param = '"' + param + '"';
                flash.Lib.getURL(new URLRequest('javascript:' + fn + '(' + param + ');void(0);'));
            } else {
                flash.external.ExternalInterface.call(fn, param);
            }
        }
    }
	
	// }}}
	// {{{ public static function _initializer()

	public static function _initializer()
	{
		if (_availability) {
			try {
				_availability = 
					cast(flash.external.ExternalInterface.call('function() { return (SWFAddress!=null); }'),Bool);
				flash.external.ExternalInterface.addCallback('getSWFAddressValue',
					function(){return SWFAddress.getValue();});
				flash.external.ExternalInterface.addCallback('setSWFAddressValue',
					function(value){SWFAddress._setValue(value);});
			} catch (e:Dynamic) {
				_availability = false;
			}    
		}
	
		_interval = new haxe.Timer(10);
		_interval.run = _check;
		return true;
	}

	// }}}
	// {{{ public static function init()

	public static function init()
	{
		_initialized = _initializer();
	}

	// }}}
	// {{{ public static function back() : Void

	public static function back() : Void
	{
		_call('SWFAddress.back');
	}

	// }}}
	// {{{ public static function forward() : Void

	public static function forward() : Void
	{
		_call('SWFAddress.forward');
	}

	// }}}
	// {{{ public static function up() : Void

	public static function up() : Void
	{
		var path:String = getPath();
		setValue(
			path.substr(
				0,
				path.lastIndexOf('/',path.length-2)
				+( (path.substr(path.length-1)=='/')?1:0 )
			)
		);
	}

	// }}}
	// {{{ public static function go( delta ) : Void

	public static function go( delta ) : Void
	{
		_call('SWFAddress.go', delta);
	}

	// }}}
	// {{{ public static function href( url:String, target:String ) : Void

	public static function href( url:String, target:String ) : Void
	{
		target = (target != null) ? target : '_self';        
		if (_availability && Capabilities.playerType == 'ActiveX') {
			flash.external.ExternalInterface.call('SWFAddress.href', url, target);
			return;
		}
		flash.Lib.getURL(new URLRequest(url), target);
	}

	// }}}
	// {{{ public static function popup(url, name, options, handler) : Void

	public static function popup(url, name, options, handler) : Void
	{
		name = ( name != null) ? name : 'popup';
		options = ( options != null) ? options : '""';
		handler = ( handler != null) ? handler : '';
		if (_availability && Capabilities.playerType == 'ActiveX') {
			flash.external.ExternalInterface.call('SWFAddress.popup', url, name, options, handler);
			return;
		}
		flash.Lib.getURL(new URLRequest('javascript:popup=window.open("'
			+ url + '","' + name + '",' + options + ');' + handler + ';void(0);'));
	}

	// }}}
	// {{{ public static function getBaseURL() : String

	public static function getBaseURL() : String
	{
		var url = 'null';
		if (_availability)
			url = Std.string(flash.external.ExternalInterface.call('SWFAddress.getBaseURL'));
		return (url == 'undefined' || url == 'null' || !_availability) ? '' : url;
	}

	// }}}
	// {{{ public static function getStrict() : Bool

	public static function getStrict() : Bool
	{
		var strict = 'null';
		if (_availability)
			strict = Std.string(flash.external.ExternalInterface.call('SWFAddress.getStrict'));
		return (strict == 'null' || strict == 'undefined') ? _strict : (strict == 'true');
	}

	// }}}
	// {{{ public static function setStrict( strict:Dynamic ) : Void

	public static function setStrict( strict:Dynamic ) : Void
	{
		_call('SWFAddress.setStrict', strict);
		_strict = strict;
	}

	// }}}
	// {{{ public static function getHistory() : Bool

	public static function getHistory() : Bool
	{
		return (_availability) ? 
			cast(flash.external.ExternalInterface.call('SWFAddress.getHistory'),Bool) : false;
	}

	// }}}
	// {{{ public static function setHistory( history ) : Void

	public static function setHistory( history ) : Void
	{
		_call('SWFAddress.setHistory', history);
	}

	// }}}
	// {{{ public static function getTracker() : String

	public static function getTracker() : String
	{
		return (_availability) ? 
			Std.string(flash.external.ExternalInterface.call('SWFAddress.getTracker')) : '';
	}

	// }}}
	// {{{ public static function setTracker( tracker ) : Void

	public static function setTracker( tracker ) : Void
	{
		_call('SWFAddress.setTracker', tracker);
	}

	// }}}
	// {{{ public static function getTitle() : String

	public static function getTitle() : String
	{
		var title = (_availability) ? 
			Std.string(flash.external.ExternalInterface.call('SWFAddress.getTitle')) : '';
		if (title == 'undefined' || title == 'null') title = '';
		return title;
	}

	// }}}
	// {{{ public static function setTitle( title ) : Void

	public static function setTitle( title ) : Void
	{
		_call('SWFAddress.setTitle', title);
	}

	// }}}
	// {{{ public static function getStatus() : String

	public static function getStatus() : String
	{
		var status = (_availability) ? 
			Std.string(flash.external.ExternalInterface.call('SWFAddress.getStatus')) : '';
		if (status == 'undefined' || status == 'null') status = '';
		return status;
	}

	// }}}
	// {{{ public static function setStatus( status ) : Void

	public static function setStatus( status ) : Void
	{
		_call('SWFAddress.setStatus', status);
	}

	// }}}
	// {{{ public static function resetStatus() : Void

	public static function resetStatus() : Void
	{
		_call('SWFAddress.resetStatus');
	}

	// }}}
	// {{{ public static function getValue() : String

	public static function getValue() : String
	{
		//return SWFAddress.decodeURI(SWFAddress._strictCheck(SWFAddress._value, false));
		return SWFAddress._strictCheck(SWFAddress._value, false);
	}

	// }}}
	// {{{ public static function setValue( value ) : Void

	public static function setValue( value ) : Void
	{
		if (value == 'undefined' || value == 'null') value = '';
		//value = SWFAddress.encodeURI(SWFAddress.decodeURI(value));
		if (SWFAddress._value == value) return;
		SWFAddress._value = value;
		_call('SWFAddress.setValue', value);
		if (SWFAddress._init) {
			SWFAddress._dispatchEvent('change');
		} else {
			SWFAddress._initChanged = true;
		}
	}

	// }}}
	// {{{ public static function getPath() : String

	public static function getPath() : String
	{
		var value = SWFAddress.getValue();
		if (value.indexOf('?') != -1) {
			return value.split('?')[0];
		} else if (value.indexOf('#') != -1) {
			return value.split('#')[0];
		} else {
			return value;   
		}
	}

	// }}}
	// {{{ public static function getPathNames() : Array<String>

	public static function getPathNames() : Array<String>
	{
		var path = SWFAddress.getPath();
		var names = path.split('/');
		if (path.substr(0, 1) == '/' || path.length == 0)
			names.splice(0, 1);
		if (path.substr(path.length - 1, 1) == '/')
			names.splice(names.length - 1, 1);
		return names;
	}

	// }}}
	// {{{ public static function getQueryString() : String

	public static function getQueryString() : String
	{
		var value = SWFAddress.getValue();
		var index = value.indexOf('?');
		if (index != -1 && index < value.length) {
			return value.substr(index + 1);
		}
		return '';
	}

	// }}}
	// {{{ public static function getParameter( param ) : String

	public static function getParameter( param ) : String
	{
		var value = SWFAddress.getValue();
		var index = value.indexOf('?');
		if (index != -1) {
			value = value.substr(index + 1);
			var params = value.split('&');
			var p, i = params.length;
			while(i>0) {
				i--;
				p = params[i].split('=');
				if (p[0] == param) {
					return p[1];
				}
			}
		}
		return '';
	}

	// }}}
	// {{{ public static function getParameterNames() : Array<String>

	public static function getParameterNames() : Array<String>
	{
		var value = SWFAddress.getValue();
		var index = value.indexOf('?');
		var names = new Array();
		if (index != -1) {
			value = value.substr(index + 1);
			if (value != '' && value.indexOf('=') != -1) {            
				var params = value.split('&');
				var i = 0;
				while(i < params.length) {
					names.push(params[i].split('=')[0]);
					i++;
				}
			}
		}
		return names;
	}

	// }}}
	// {{{ public static function toString() : String

	public static function toString() : String
	{
		return '[class SWFAddress]';
	}

	// }}}
	// {{{ public static function encodeURI( str:String ) : String

	public static function encodeURI( str:String ) : String
	{
		// {{{ encodeURI
		// Ported from iecompat.js <http://www.coolcode.cn/?action=show&id=126>
		var l = ['%00', '%01', '%02', '%03', '%04', '%05', '%06', '%07', '%08', 
						'%09', '%0A', '%0B', '%0C', '%0D', '%0E', '%0F', '%10', 
						'%11', '%12', '%13', '%14', '%15', '%16', '%17', '%18', 
						'%19', '%1A', '%1B', '%1C', '%1D', '%1E', '%1F', '%20', 
						'!', '%22', '#', '$', '%25', '&', '\'', '(', ')', '*', 
						'+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', 
						'6', '7', '8', '9', ':', ';', '%3C', '=', '%3E', '?',
						'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 
						'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 
						'V', 'W', 'X', 'Y', 'Z', '%5B', '%5C', '%5D', '%5E', '_', 
						'%60', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 
						'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',  'u', 
						'v', 'w', 'x', 'y', 'z', '%7B', '%7C', '%7D', '~', '%7F'];
						
		var out = [], i, j, len = str.length, c, c2;

		//for (i = 0, j = 0; i < len; i++) {
		var j = 0;
		var i = 0;
		while ( i<len ) {
			c = str.charCodeAt(i);
			i++;
			if (c <= 0x007F) {
					out[j++] = l[c];
					continue;
			} else if (c <= 0x7FF) {
					out[j++] = '%' + StringTools.hex(0xC0 | ((c >>  6) & 0x1F),2).toUpperCase();
					out[j++] = '%' + StringTools.hex(0x80 |         (c & 0x3F),2).toUpperCase();
					continue;
			} else if (c < 0xD800 || c > 0xDFFF) {
					out[j++] = '%' + StringTools.hex(0xE0 | ((c >> 12) & 0x0F),2).toUpperCase();
					out[j++] = '%' + StringTools.hex(0x80 | ((c >>  6) & 0x3F),2).toUpperCase();
					out[j++] = '%' + StringTools.hex(0x80 |         (c & 0x3F),2).toUpperCase();
					continue;
			} else {
				if (i < len) {
					i++;
					c2 = str.charCodeAt(i);
					if (c <= 0xDBFF && 0xDC00 <= c2 && c2 <= 0xDFFF) {
						c = ((c & 0x03FF) << 10 | (c2 & 0x03FF)) + 0x010000;
						if (0x010000 <= c && c <= 0x10FFFF) {
								out[j++] = '%' + StringTools.hex(0xF0 | ((c >>> 18) & 0x3F),2).toUpperCase();
								out[j++] = '%' + StringTools.hex(0x80 | ((c >>> 12) & 0x3F),2).toUpperCase();
								out[j++] = '%' + StringTools.hex(0x80 | ((c >>>  6) & 0x3F),2).toUpperCase();
								out[j++] = '%' + StringTools.hex(0x80 |          (c & 0x3F),2).toUpperCase();
								continue;
						}
					}
				}
			}
			return null;
		}
		return out.join('');
		// }}}
	}

	// }}}
	// {{{ public static function decodeURI( str:String ) : String

	public static function decodeURI( str:String ) : String
	{
		// {{{ decodeURI
		// Ported from iecompat.js <http://www.coolcode.cn/?action=show&id=126>
		var out:Array<String> = [], i = 0, j = 0, len = str.length;
		var c, c2, c3, c4, s;

		var checkcode = function (strcc, i1, i2):Null<Int> {
			var d1 = strcc.charAt(i1),
				d2 = strcc.charAt(i2);
			if (Math.isNaN(Std.parseInt(d1)) || Math.isNaN(Std.parseInt(d2))) {
				return null;
			}
			return Std.parseInt(d1 + d2);
		}

		var checkutf8 = function (strcu, i1, i2, i3) {
			var ccu:Null<Int> = strcu.charCodeAt(i1);
			if (ccu == 37) {
				if ((ccu = checkcode(strcu, i2, i3)) == null) return null;
			}
			if ((ccu >> 6) != 2) {
				return null;
			}
			return ccu;
		}

		while(i < len) {
			c = str.charCodeAt(i++);
			if (c == 37) {
				if ((c = checkcode(str, i++, i++)) == null) return null;
			} else {
				out[j++] = String.fromCharCode(c);
				continue;
			}
			switch(c) {
				case 35: case 36: case 38: case 43: case 44: case 47:
				case 58: case 59: case 61: case 63: case 64: {
					if (str.charCodeAt(i - 3) == 37) {
						out[j++] = str.substr(i - 3, 3);
					} else {
						out[j++] = str.substr(i - 1, 1);
					}
					break;
				}
				default: {
					switch (c >> 4) { 
						case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: {
							// 0xxxxxxx
							out[j++] = String.fromCharCode(c);
							break;
						}
						case 12: case 13: {
							// 110x xxxx    10xx xxxx
							if ((c2 = checkutf8(str, i++, i++, i++)) == null) return null;
							out[j++] = String.fromCharCode(((c & 0x1F) << 6) | (c2 & 0x3F));
							break;
						}
						case 14: {
							// 1110 xxxx  10xx xxxx  10xx xxxx
							if ((c2 = checkutf8(str, i++, i++, i++)) == null) return null;
							if ((c3 = checkutf8(str, i++, i++, i++)) == null) return null;
							out[j++] = String.fromCharCode(((c & 0x0F) << 12) |
								((c2 & 0x3F) << 6) | ((c3 & 0x3F) << 0));
							break;
						}
						default: {
							switch (c & 0xf) {
								case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: {
									// 1111 0xxx  10xx xxxx  10xx xxxx  10xx xxxx
									if ((c2 = checkutf8(str, i++, i++, i++)) == null) return null;
									if ((c3 = checkutf8(str, i++, i++, i++)) == null) return null;
									if ((c4 = checkutf8(str, i++, i++, i++)) == null) return null;
									s = ((c  & 0x07) << 18) |
										((c2 & 0x3f) << 12) |
										((c3 & 0x3f) <<  6) |
										(c4 & 0x3f) - 0x10000;
									if (0 <= s && s <= 0xfffff) {
										out[j++] =
											String.fromCharCode(((s >>> 10) & 0x03ff) | 0xd800)
											+ String.fromCharCode( (s & 0x03ff) | 0xdc00);
									} else {
										return null;
									}
									break;
								}
								default: {
									return null;
								}
							}
						}
					}
				}
			}
		}
		return out.join('');
		// }}}
	}

	// }}}
}



